package uk.co.q3c.deplan.server.unit.domain.task;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;

import org.apache.log4j.Logger;
import org.testng.Assert;
import org.testng.annotations.Test;

import uk.co.q3c.deplan.client.domain.DomainObject;
import uk.co.q3c.deplan.client.domain.PropertyChangeEvent;
import uk.co.q3c.deplan.client.domain.PropertyChangeListener;
import uk.co.q3c.deplan.client.domain.dag.ChangeInvalidException;
import uk.co.q3c.deplan.client.domain.dag.CycleDetectedException;
import uk.co.q3c.deplan.client.domain.resource.IndividualResource;
import uk.co.q3c.deplan.client.domain.resource.PersistenceReferenceException;
import uk.co.q3c.deplan.client.domain.resource.Resource;
import uk.co.q3c.deplan.client.domain.resource.ResourceGroup;
import uk.co.q3c.deplan.client.domain.resource.ResourcePool;
import uk.co.q3c.deplan.client.domain.task.BaseTask;
import uk.co.q3c.deplan.client.domain.task.DependencyLoopException;
import uk.co.q3c.deplan.client.domain.task.StartFinishTimes;
import uk.co.q3c.deplan.client.domain.task.Task;
import uk.co.q3c.deplan.client.domain.task.TaskReference;
import uk.co.q3c.deplan.server.util.Q3Calendar;

/**
 * Unit test for {@link Task}
 * 
 * @author DSowerby 18 Jul 2008 21:08:07
 * 
 */
@Test
public class Task_UT extends DomainBase_UT {
	boolean testBatchReady;
	TestListener testListener;
	String testBatchResult;
	ResourcePool pool;
	ArrayList<DomainObject> changedModelObjects = new ArrayList<DomainObject>();
	ArrayList<String> propertiesFired = new ArrayList<String>();
	BaseTask task1;
	BaseTask task2;
	BaseTask task3;
	BaseTask task4;
	BaseTask task5;
	Resource r1;
	Resource r2;

	protected final Logger logger = Logger.getLogger(getClass().getName());

	class TestListener implements PropertyChangeListener {

		@Override
		public void propertyChange(PropertyChangeEvent evt) {
			DomainObject t = evt.getSource();
			if (evt.getPropertyName()
					.equals("_test")) {
				testBatchResult = propertiesFired.toString();
				testBatchReady = true;
			}
			changedModelObjects.add(t);
			propertiesFired.add(evt.getPropertyName());
			logger.info("fired " + evt.getPropertyName() + " from " + t.getClass()
					.getSimpleName());

		}

	}

	@Override
	protected void beforeMethod() throws Exception {
		super.beforeMethod();
		connection.openSession();
		pool = new ResourcePool(connection);
		buildPlan();
		changedModelObjects.clear();

	}

	@Override
	protected void afterMethod() throws Exception {
		pool = null;
		connection.closeSession();
		super.afterMethod();
	}

	/**
	 * Note: this test does not use a session cache so equality of instances is determined by keys or ids- java references will be different
	 * for different instances of the same object
	 * 
	 * @throws CycleDetectedException
	 * 
	 * @throws CycleDetectedException
	 * @throws ChangeInvalidException
	 * @throws PersistenceReferenceException
	 */
	@Test
	public void parenting() throws CycleDetectedException, ChangeInvalidException, PersistenceReferenceException {
		Assert.assertTrue(task1.getParentTask() == null);
		Assert.assertTrue(task2.getParentTaskKey()
				.equals(task1.getKey()));
		Assert.assertTrue(task3.getParentTaskKey()
				.equals(task2.getKey()));

		// subTasks added?
		ArrayList<Task> subtasks = task1.subTasksList();
		Assert.assertEquals(subtasks.toString(), "[2]");
		subtasks = task2.subTasksList();
		Assert.assertEquals(subtasks.toString(), "[3]");

		Assert.assertTrue(connection.taskService()
				.taskIsAtomic(task3));
		Assert.assertFalse(connection.taskService()
				.taskIsAtomic(task2));

		BaseTask st1 = createStandardTask(1);
		BaseTask st2 = createStandardTask(2);
		BaseTask st3 = createStandardTask(3);
		st2.setParentTask(st1);
		st3.setParentTask(st2);
		st1.save();
		st2.save();
		st3.save();

		// try and set a bad parent (one that causes a loop)
		// it should remain as it was
		try {
			st1.setParentTask(st3);
		} catch (CycleDetectedException e) {
			// do nothing for test
		}
		Assert.assertNull(st1.getParentTask(), "parent loop");
		// null a parent
		st3.setParentTask(null);
		Assert.assertNull(st3.getParentTask(), "parent nulled");
	}

	@Test
	public void workDone() throws PersistenceReferenceException {
		IndividualResource ds = addResource("dsowerby");
		IndividualResource mp = addResource("mpaine");
		ResourceGroup g1 = addResourceGroup("g1", "dsowerby", "mpaine");

		// check total work done correct when done by different resources on
		// different dates
		BaseTask task1 = createStandardTask(1);
		task1.setName("task1");
		task1.assignResource(g1, 1);
		Calendar di1 = new GregorianCalendar();
		task1.addToWorkDone(50, ds, di1.getTime());
		Assert.assertEquals(task1.getWorkDone(), 50, "cumulative work done");
		Q3Calendar di2 = new Q3Calendar(di1.getTimeInMillis());
		di2.incDay();
		task1.addToWorkDone(5, mp, di2.getTime());
		Assert.assertEquals(task1.getWorkDone(), 55, "cumulative work done");
	}

	@Test
	public void qualifiedName() throws CycleDetectedException, ChangeInvalidException, PersistenceReferenceException {
		task3.setParentTask(task2);

		Assert.assertEquals(task3.qualifiedName(), "1.2.3");
		Assert.assertEquals(task1.qualifiedName(), "1");

	}

	@Test
	public void dependencies_usingAddPredecessor() throws PersistenceReferenceException {
		// add a null, should debug message
		task4.addPredecessor(null);

		task5.addPredecessor(task4);
		task4.addPredecessor(task3);
		Assert.assertEquals(task3.successors()
				.toString(), "[4]");
		Assert.assertEquals(task4.successors()
				.toString(), "[5]");
		Assert.assertEquals(task5.successors()
				.toString(), "[]");
		Assert.assertEquals(task3.predecessors()
				.toString(), "[]");
		Assert.assertEquals(task4.predecessors()
				.toString(), "[3]");
		Assert.assertEquals(task5.predecessors()
				.toString(), "[4]");
	}

	@Test
	public void dependencies_usingAddSuccessor() throws PersistenceReferenceException {
		// add a null, should debug message
		task4.addSuccessor(null);

		task4.addSuccessor(task5);
		task3.addSuccessor(task4);
		Assert.assertEquals(task3.successors()
				.toString(), "[4]");
		Assert.assertEquals(task4.successors()
				.toString(), "[5]");
		Assert.assertEquals(task5.successors()
				.toString(), "[]");
		Assert.assertEquals(task3.predecessors()
				.toString(), "[]");
		Assert.assertEquals(task4.predecessors()
				.toString(), "[3]");
		Assert.assertEquals(task5.predecessors()
				.toString(), "[4]");
	}

	// these tests not valid now that BaseTask has separate inheritance
	// @Test(expectedExceptions = { AbstractTaskNotDependencyException.class })
	// public void dependencies_toSummary_Predecessor() {
	// task1.addPredecessor(task4);
	// }
	//
	// @Test(expectedExceptions = { AbstractTaskNotDependencyException.class })
	// public void dependencies_toSummary_Successor() {
	// task1.addSuccessor(task4);
	// }

	@Test(expectedExceptions = { DependencyLoopException.class })
	public void dependencies_loopOnSuccessor() throws PersistenceReferenceException {
		task4.addSuccessor(task5);
		task3.addSuccessor(task4);
		task5.addSuccessor(task3);
	}

	@Test(expectedExceptions = { DependencyLoopException.class })
	public void dependencies_loopOnPredecessor() throws PersistenceReferenceException {
		task5.addPredecessor(task5);
		task4.addPredecessor(task3);
		task3.addPredecessor(task5);
	}

	@Test(expectedExceptions = { DependencyLoopException.class })
	public void dependencyOnSelf_addPredecessor() throws PersistenceReferenceException {
		task5.addPredecessor(task5);
	}

	@Test(expectedExceptions = { DependencyLoopException.class })
	public void dependencyOnSelf_addSuccessor() throws PersistenceReferenceException {
		task5.addSuccessor(task5);
	}

	@Test
	public void dependenciesComplete() throws PersistenceReferenceException {
		// depends on nothing
		Assert.assertTrue(task5.predecessorsAllocated());
		// depends on one incomplete task
		task5.addPredecessor(task4);
		task4.setWorkRemaining(1);
		Assert.assertFalse(task5.predecessorsAllocated());

		// depends on one complete task
		task4.setWorkRemaining(0);
		Assert.assertTrue(task5.predecessorsAllocated());

		// one complete, one incomplete
		task5.addPredecessor(task4);
		task5.addPredecessor(task3);
		task4.setWorkRemaining(0);
		task3.setWorkRemaining(1);
		Assert.assertFalse(task5.predecessorsAllocated());

		// more than one complete
		task5.addPredecessor(task4);
		task5.addPredecessor(task3);
		task4.setWorkRemaining(0);
		task3.setWorkRemaining(0);
		Assert.assertTrue(task5.predecessorsAllocated());
	}

	public void assignResources() throws CycleDetectedException, ChangeInvalidException, PersistenceReferenceException {
		buildPlan();
		buildResources();
		BaseTask AbstractTask = createStandardTask(1);
		AbstractTask.setAssignedResource(r1);
		AbstractTask.setAssignedUnits(1);
		Assert.assertEquals(AbstractTask.getAssignedResource()
				.getName(), "dsowerby");
	}

	public void allocateWork() throws PersistenceReferenceException {
		BaseTask AbstractTask = createStandardTask(1);
		AbstractTask.setWorkRemaining(1000);
		AbstractTask.assignResource(r1, 1);
		// these are set to null by the levelling process normally
		AbstractTask.setStartRemaining(null);
		AbstractTask.setPlannedFinish(null);

		StartFinishTimes sft = new StartFinishTimes();

		Calendar calStart = new GregorianCalendar();
		Calendar calFinish = new GregorianCalendar();
		Calendar overallStart = new GregorianCalendar();
		int workToAllocate = 400;

		// 400 of a 1000
		calStart.set(2008, 03, 01, 9, 0, 0);
		overallStart.setTimeInMillis(calStart.getTimeInMillis());
		sft.start = calStart.getTimeInMillis();
		calFinish.setTimeInMillis(calStart.getTimeInMillis());
		calFinish.add(Calendar.MINUTE, workToAllocate);
		sft.finish = calFinish.getTimeInMillis();

		AbstractTask.allocateWork(workToAllocate, sft);
		Assert.assertEquals(AbstractTask.unallocatedWork(), 1000 - workToAllocate);
		logger.info(new Date(sft.start));
		Assert.assertEquals(AbstractTask.getStart(), overallStart.getTime());
		Assert.assertEquals(AbstractTask.getPlannedFinish(), calFinish.getTime());

		// 800 of a 1000
		calStart.add(Calendar.DAY_OF_MONTH, 1);
		sft.start = calStart.getTimeInMillis();
		calFinish.setTimeInMillis(calStart.getTimeInMillis());
		calFinish.add(Calendar.MINUTE, workToAllocate);
		sft.finish = calFinish.getTimeInMillis();

		AbstractTask.allocateWork(workToAllocate, sft);
		Assert.assertEquals(AbstractTask.unallocatedWork(), 1000 - workToAllocate * 2);
		Assert.assertEquals(AbstractTask.getStart(), overallStart.getTime());
		Assert.assertEquals(AbstractTask.getPlannedFinish(), calFinish.getTime());

		// 1200 of a 1000, should plannedFinish today
		calStart.add(Calendar.DAY_OF_MONTH, 1);
		sft.start = calStart.getTimeInMillis();
		calFinish.setTimeInMillis(calStart.getTimeInMillis());
		calFinish.add(Calendar.MINUTE, workToAllocate);
		sft.finish = calFinish.getTimeInMillis();

		AbstractTask.allocateWork(workToAllocate, sft);
		Assert.assertEquals(AbstractTask.unallocatedWork(), -200);
		Assert.assertEquals(AbstractTask.getStart(), overallStart.getTime());
		Assert.assertEquals(AbstractTask.getPlannedFinish(), calFinish.getTime());
		Assert.assertTrue(AbstractTask.isFullyAllocated());
	}

	/**
	 * See {@link Task#setDiscarded(boolean)}. Should remove all references to this task
	 * 
	 * @throws CycleDetectedException
	 * @throws ChangeInvalidException
	 * @throws PersistenceReferenceException
	 */
	public void discardAtomic() throws CycleDetectedException, ChangeInvalidException, PersistenceReferenceException {
		task4.setName("target");
		task4.setParentTask(task1);
		task1.setName("parent");
		task4.addPredecessor(task3);
		task3.setName("predecessor");
		task4.addSuccessor(task5);
		task5.setName("successor");

		task1.addPropertyChangeListener(new TestListener());
		task3.addPropertyChangeListener(new TestListener());
		task5.addPropertyChangeListener(new TestListener());

		Assert.assertTrue(task5.getPredecessors()
				.contains(task4));
		Assert.assertTrue(task3.getSuccessors()
				.contains(task4));

		task4.setDiscarded(true);

		// References from other tasks should be gone
		Assert.assertFalse(task3.getSuccessors()
				.contains(task4));
		Assert.assertFalse(task5.getPredecessors()
				.contains(task4));
		// parent is no longer referenced
		Assert.assertFalse(task1.hasSubTask(task4));

	}

	/**
	 * Should remove references, and set discarded for the target and its subtasks. Subtasks must retain a reference to its parent
	 * 
	 * @throws CycleDetectedException
	 * @throws ChangeInvalidException
	 * @throws PersistenceReferenceException
	 */
	public void discardSummary() throws CycleDetectedException, ChangeInvalidException, PersistenceReferenceException {

		task4.setName("child1");
		task4.setParentTask(task1);
		task1.setName("target");
		task4.addPredecessor(task3);
		task3.setName("predecessor");
		task4.addSuccessor(task5);
		task5.setName("successor");
		BaseTask project = createProject("66");
		project.setName("project");
		task1.setParentTask(project);
		BaseTask task6 = createStandardTask(22);
		task6.setName("child2");
		task6.setParentTask(task1);

		task4.save();
		task1.save();
		task3.save();
		task5.save();
		project.save();
		task6.save();

		// check setup
		Assert.assertTrue(project.hasSubTask(task1));
		Assert.assertTrue(task1.hasSubTask(task4));
		Assert.assertTrue(task1.hasSubTask(task6));
		Assert.assertTrue(task4.hasSuccessor(task5));
		Assert.assertTrue(task4.hasPredecessor(task3));

		task1.setDiscarded(true);
		// task1 subtasks should be discarded too
		Assert.assertTrue(task1.getDiscarded());
		Assert.assertTrue(task6.getDiscarded());
		Assert.assertTrue(task4.getDiscarded());
		// but subTasks should still have task1 as parent
		Assert.assertEquals(task6.getParentTask(), task1);
		Assert.assertEquals(task4.getParentTask(), task1);
		// and task1's parent should no longer have task1 as a subtask
		Assert.assertFalse(project.hasSubTask(task1));
		// dependencies should have been dropped
		Assert.assertFalse(task4.hasSuccessor(task5));
		Assert.assertFalse(task4.hasPredecessor(task3));

	}

	public void subTasksListAndArray() throws CycleDetectedException, ChangeInvalidException, PersistenceReferenceException {
		buildPlan();
		task4.setParentTask(task1);
		task5.setParentTask(task1);
		Assert.assertEquals(task1.subTasksList()
				.size(), 3);

	}

	public void setParent() throws CycleDetectedException, ChangeInvalidException, PersistenceReferenceException {
		BaseTask root = createStandardTask("root");
		// root.setName("root");
		root.save();

		BaseTask a = createStandardTask("a");
		// a.setName("a");
		a.save();
		a.setPriority(100);
		a.setParentTask(root);

		BaseTask a1 = new BaseTask(connection);
		a1.setName("a1");
		a1.save();
		a1.setPriority(200);
		a1.setParentTask(a);
		a1.save();

		Task a1a1 = new BaseTask(connection);
		a1a1.setName("a1a1");
		a1a1.save();
		a1a1.setPriority(100);
		a1a1.setParentTask(a1);
		a1a1.save();

		Task a1a2 = new BaseTask(connection);
		a1a2.setName("a1a2");
		a1a2.save();
		a1a2.setPriority(100);
		a1a2.setParentTask(a1);
		a1a2.save();

		Task b = new BaseTask(connection);
		b.setName("b");
		b.save();
		b.setPriority(100);
		b.setParentTask(root);
		Assert.assertEquals(root.subTaskCount(), 2);
	}

	public void startFinish() throws PersistenceReferenceException {
		Task st = new BaseTask(connection);
		// defaults to 0 if none set
		Assert.assertEquals(st.startTime(), 0);
		Assert.assertEquals(st.finishTime(), 0);
		Assert.assertEquals(st.start()
				.getTime(), 0);
		Assert.assertEquals(st.finish()
				.getTime(), 0);
	}

	public void rootAndfindById() throws CycleDetectedException, ChangeInvalidException, PersistenceReferenceException {
		BaseTask st1 = new BaseTask(connection);
		connection.taskService()
				.save(st1);
		long st1id = st1.getTaskId();
		// attempt to confuse by having the same as an atomic task
		BaseTask st2 = new BaseTask(connection);
		connection.taskService()
				.save(st2);
		long st2id = st2.getTaskId();
		// attempt to confuse by having unique then trying to find it
		st2.setParentTask(st1);
		connection.taskService()
				.save(st2);
		Task at1 = new BaseTask(connection);
		st1.save();
		at1.save();
		at1.setParentTask(st1);
		at1.setName("at1");
		connection.taskService()
				.save(at1);
		long at1id = at1.getTaskId();
		Task at2 = new BaseTask(connection);
		at2.save();
		at2.setParentTask(st1);
		at2.setName("at2");
		connection.taskService()
				.save(at2);
		long at2id = at2.getTaskId();
		Task at3 = new BaseTask(connection);
		at3.save();
		at3.setParentTask(st2);
		at3.setName("at3");
		connection.taskService()
				.save(at3);
		long at3id = at3.getTaskId();

		// whichever one is used to call root() should give the same answer
		Assert.assertEquals(st1, at1.root());
		Assert.assertEquals(st1, at2.root());
		Assert.assertEquals(st1, at3.root());
		// including root itself
		Assert.assertEquals(st1, st1.root());
		Assert.assertEquals(st1, st2.root());
	}

	public void setDependencies() throws PersistenceReferenceException {
		// set up some predecessors
		task5.addPredecessor(task3);
		task5.addPredecessor(task4);
		// confirm all as expected
		Assert.assertTrue(task5.hasPredecessor(task3));
		Assert.assertTrue(task3.hasSuccessor(task5));
		Assert.assertTrue(task4.hasSuccessor(task5));

		// create new dependency set
		BaseTask task6 = createStandardTask(6);

		// set new dependencies
		task5.addPredecessor(task6);
		// check new dependencies
		Assert.assertTrue(task5.hasPredecessor(task6));
		Assert.assertTrue(task6.hasSuccessor(task5));

	}

	/**
	 * Checks to see that property change events are fired when expected. Uses TestListener to capture the events
	 * 
	 * @throws CycleDetectedException
	 * @throws ChangeInvalidException
	 * @throws PersistenceReferenceException
	 */
	public void propertyChange() throws CycleDetectedException, ChangeInvalidException, PersistenceReferenceException {
		IndividualResource ds = addResource("dsowerby");

		BaseTask pt1 = newTask("pt1");
		pt1.assignResource(ds, 1);
		testListener = new TestListener();
		task1.addPropertyChangeListener(testListener);

		ArrayList<Task> preds = new ArrayList<Task>();
		preds.add(task4);
		ArrayList<Task> succs = new ArrayList<Task>();
		succs.add(task5);

		BaseTask t = newTask("");
		t.addPropertyChangeListener(testListener);
		t.assignResource(ds, 1);

		startBatch();
		t.setActualFinish(new Date());
		t.setActualStart(new Date());
		IndividualResource ir = new IndividualResource(connection);
		ir.save();
		t.setAssignedResource(ir);
		t.setAssignedUnits(2);
		t.setDuration(23);
		endBatch(t);
		Assert.assertEquals(testBatchResult, "[actualFinish, actualStart, assignedResource, assignedUnits, duration]");

		// startBatch();
		// t.setPlannedFinish(new Date());
		// t.setFixedDuration(true);
		// t.setName("www");
		// t.save();
		// BaseTask st1 = new BaseTask(connection);
		// st1.save();
		// t.setParentTask(st1);
		// t.setPriority(300);
		// endBatch(t);
		// Assert.assertEquals(testBatchResult, "[plannedFinish, fixedDuration, name, parentTask, priority]");

		startBatch();
		t.setSpecification("widget");
		t.setStartRemaining(new Date());
		t.save();
		// this won't fire while there's only one choice
		// t.setTaskConstraint(TaskConstraint.ASAP);
		endBatch(t);
		Assert.assertEquals(testBatchResult, "[specification, startRemaining]");

		startBatch();
		// these cause multiple changes in the 3 work fields
		t.setWorkDone(56, ds, new Date());
		t.setWorkRemaining(345);
		endBatch(t);
		Assert.assertEquals(testBatchResult, "[workDone, actualFinish, workRemaining]");

		startBatch();
		// these cause multiple changes in the 3 work fields
		t.addToWorkDone(45, ds, new Date());
		t.setWorkRemaining(300);
		endBatch(t);
		Assert.assertEquals(testBatchResult, "[workDone, actualFinish, workRemaining]");

		startBatch();
		for (Task at : preds) {
			t.addPredecessor(at);
		}

		for (Task at1 : succs) {
			t.addSuccessor(at1);
		}

		endBatch(t);
		Assert.assertEquals(testBatchResult, "[predecessor, successor]");

		startBatch();
		BaseTask s = createStandardTask(12);
		BaseTask p = createStandardTask(13);
		t.addSuccessor(s);
		t.addPredecessor(p);
		t.removeSuccessor(s);
		t.removePredecessor(p);
		endBatch(t);
		Assert.assertEquals(testBatchResult, "[successor, predecessor, successor, predecessor]");

		// false calls (already gone)
		// so shouldn't fire

		startBatch();
		t.removeSuccessor(s);
		t.removePredecessor(p);
		endBatch(t);
		Assert.assertEquals(testBatchResult, "[]");

		startBatch();
		t.setStartRemaining(new Date());
		t.setActualStart(new Date());
		t.setActualFinish(new Date());
		endBatch(t);
		Assert.assertEquals(testBatchResult, "[startRemaining, actualStart, actualFinish]");

		startBatch();
		t.setTargetStartDate(new Date());
		endBatch(t);
		Assert.assertEquals(testBatchResult, "[targetStartDate]");

		startBatch();
		t.setTargetFinishDate(new Date());
		endBatch(t);
		Assert.assertEquals(testBatchResult, "[targetFinishDate]");

		startBatch();
		BaseTask st = createStandardTask(45);
		st.addPropertyChangeListener(testListener);
		t.setParentTask(st);
		endBatch(t);
		Assert.assertEquals(testBatchResult, "[parentTask]");

		startBatch();
		t.setParentTask(null);
		endBatch(t);
		Assert.assertEquals(testBatchResult, "[parentTask]");

	}

	@Test(enabled = false)
	public void whereUsed() {
		List<TaskReference> references = new ArrayList<TaskReference>();
		Assert.fail("Where used not implemented");
		// task3.whereUsed(references);
		// should be parent only
		Assert.assertEquals(references.size(), 1);
	}

	public void hasPredSucc() throws PersistenceReferenceException {
		BaseTask a = createStandardTask(1);
		BaseTask b = createStandardTask(2);
		Assert.assertFalse(a.hasSuccessor(b));
		Assert.assertFalse(a.hasPredecessor(b));
		Assert.assertFalse(b.hasSuccessor(a));
		Assert.assertFalse(b.hasPredecessor(a));
		a.addSuccessor(b);
		Assert.assertTrue(a.hasSuccessor(b));
		Assert.assertFalse(a.hasPredecessor(b));
		Assert.assertFalse(b.hasSuccessor(a));
		Assert.assertTrue(b.hasPredecessor(a));
	}

	/**
	 * Do settings of projectTask, project and service work together properly or end up with illegal combinations?
	 */
	public void taskSettings() {
		BaseTask task = createStandardTask("base");

		task.setProject();
		Assert.assertFalse(task.isService());
		Assert.assertTrue(task.isProject());
		Assert.assertFalse(task.isServiceTask());
		Assert.assertTrue(task.isProjectTask());

		task.setService();
		Assert.assertTrue(task.isService());
		Assert.assertFalse(task.isProject());
		Assert.assertTrue(task.isServiceTask());
		Assert.assertFalse(task.isProjectTask());

		task.setProjectTask();
		Assert.assertFalse(task.isService());
		Assert.assertFalse(task.isProject());
		Assert.assertFalse(task.isServiceTask());
		Assert.assertTrue(task.isProjectTask());

		task.setServiceTask();
		Assert.assertFalse(task.isService());
		Assert.assertFalse(task.isProject());
		Assert.assertTrue(task.isServiceTask());
		Assert.assertFalse(task.isProjectTask());
	}

	private void buildResources() {
		r1 = new IndividualResource(connection);
		r1.setName("dsowerby");
		r1.save();

		r2 = new IndividualResource(connection);
		r2.setName("mpaine");
		r2.save();
	}

	private void buildPlan() throws CycleDetectedException, ChangeInvalidException, PersistenceReferenceException {
		task1 = createStandardTask(1);
		task2 = createStandardTask(2);
		task3 = createStandardTask(3);
		task2.setParentTask(task1);
		task3.setParentTask(task2);
		task4 = createStandardTask(4);
		task5 = createStandardTask(5);
		task1.save();
		task2.save();
		task3.save();
		task4.save();
		task5.save();

	}

	/**
	 * Preps change listener for a batch of property changes
	 */
	private void startBatch() {
		testBatchReady = false;
		changedModelObjects.clear();
		propertiesFired.clear();
		logger.info("start batch");
	}

	/**
	 * Ends a batch of property changes, and waits for listener to complete. The task parameter is just used to fire an end of test message
	 * - it doesn't matter which task it is
	 */
	private void endBatch(Task task) {
		int timeout = 1000;
		task.fireTestProperty();
		int i = 0;
		while (!testBatchReady && i < timeout) {
			i++;
		}
		if (i >= timeout) {
			System.out.println("TIMED OUT");
			Assert.fail();
		} else {
			System.out.println("Count while waiting: " + i);
		}
	}

	private IndividualResource addResource(String name) throws PersistenceReferenceException {
		IndividualResource r1 = new IndividualResource(connection);
		r1.setName(name);
		r1.save();
		pool.addResource(r1);
		return r1;
	}

	private BaseTask newTask(String name) {
		BaseTask pt = new BaseTask(connection);
		pt.setName(name);
		return pt;
	}

	private ResourceGroup addResourceGroup(String groupName, String r1name, String r2name) throws PersistenceReferenceException {
		ResourceGroup rg = new ResourceGroup(connection);
		rg.addResource((IndividualResource) pool.getResource(r1name));
		rg.addResource((IndividualResource) pool.getResource(r2name));
		rg.setName(groupName);
		rg.save();
		pool.addResource(rg);
		return rg;
	}
}
